/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.heron-language.com
*/

#ifndef LLSCHEME_SEXPR_HPP
#define LLSCHEME_SEXPR_HPP

namespace lls
{		
	struct Atom;

	struct CustomData 
	{
		virtual ~CustomData() {}
		
		template<typename T>
		T* As() {
			T* r = dynamic_cast<T*>(this);
			Assert(r != NULL);
			return r;
		}

		virtual CustomData* Clone() = 0;			
		
		virtual String ToString() {
			return "";
		}
	};

	struct SExpr
	{
		// a user-defined pointer 
		//SExpr* user; 

		typedef std::vector<SExpr*> coll_type;
		typedef coll_type::iterator iter_type;
		coll_type children;
		
		SExpr* parent;
		CustomData* custom;

		SExpr() 
			: parent(NULL), custom(NULL) 
		{
		}

		// Tests that the clone mechanism works (returns the correct type)
		// And also makes sure that deletion won't throw an error.
		void Test() {
			String s = Label();
			SExpr* r = Clone();
			Assert(r->Label() == s);
			Assert((custom == NULL) == (r->custom == NULL));
			Assert(r->parent == NULL);
			Assert(GetNumChildren() == r->GetNumChildren());
			Assert(GetIndex() > 0);
			delete r;
		}

		virtual ~SExpr() {
			DeletePointers(children);
			if (custom != NULL)
				delete custom;
		}
		
		template<typename T>
		static SExpr* Create() {
			return new SExpr(owner);
		};

		virtual String ToString() {
			return Label();
		}

		// Return the label: head of the S-expr
		virtual String Label() = 0;
		
		// Gets the derived type to create itself.
		virtual SExpr* NewInstance() = 0;

		int GetNumChildren() {
			return children.size();
		}

		SExpr* GetChild(int i) {
			return children[i];
		}

		// Casts the pointer 
		template<typename T>
		T* As() {
			T* r = dynamic_cast<T*>(this);
			Assert(r != NULL);
			return r;
		}

		template<typename T>
		bool Is() {
			return dynamic_cast<T*>(this) != NULL;
		}

		template<typename T>
		T* CreateChild() {
			T* r = new T();
			AddChild(r);
			return r;
		}

		void AddChild(NonNull<SExpr> x) {
			Assert(!Is<Atom>());
			children.push_back(x);
			x->parent = this;
			int n = IndexOf(x);
			Assert(n == GetNumChildren() - 1);
			n = x->GetIndex();
			Assert(n == GetNumChildren() - 1);
		}

		void InsertChild(NonNull<SExpr> after, NonNull<SExpr> x) {
			Assert(!Is<Atom>());
			int n = IndexOf(after);
			Assert(n >= 0);
			children.insert(children.begin() + n + 1, x);
			x->parent = this;
		}

		int IndexOf(NonNull<SExpr> x) {
			for (int i=0; i < GetNumChildren(); ++i) {
				if (GetChild(i) == x)
					return i;
			}
			return -1;
		}		

		int GetIndex() {
			Assert(parent != NULL);
			return parent->IndexOf(this);
		}

		void ReplaceChild(int n, NonNull<SExpr> x) {
			Assert(n >= 0 && n < GetNumChildren());
			SExpr* old = GetChild(n);
			delete(old);
			children[n] = x;
			x->parent = this;
			Assert(x->GetIndex() == n);
		}

		void DeleteChild(int n) {
			delete(DetachChild(n));
		}

		SExpr* DetachChild(int n) {
			SExpr* x = children[n];
			children.erase(children.begin() + n);
			return x;
		}

		void DeleteSelf() {
			Assert(parent != NULL);
			parent->DeleteChild(GetIndex());
		}

		SExpr* Clone() {
			SExpr* r = NewInstance();
			for (iter_type i = children.begin(); i != children.end(); ++i) {
				r->AddChild((*i)->Clone());
			}
			if (custom != NULL) 
				r->custom = custom->Clone();

			return r;
		}

		template<typename F>
		void ForEach(F& f) {
			for (iter_type i = children.begin(); i != children.end(); ++i) {
				f(*i);
			}
		}

		template<typename F>
		bool QueryAny(F& f) {
			for (iter_type i = children.begin(); i != children.end(); ++i) {
				if (f(*i))
					return true;
			}
			return false;
		}

		template<typename F>
		bool QueryAll(F& f) {
			for (iter_type i = children.begin(); i != children.end(); ++i) {
				if (!f(*i))
					return false;
			}
			return true;
		}

		SExpr* GetParent() {
			return parent;
		}
	};

	/// Paramaterized S-Expression
	template<typename T>
	struct PSExpr : SExpr {
		T val;
		String name; 
		PSExpr(const T& x, ConstString s)
			: val(x), name(s)
		{ }
		T& Param() { 
			return val;
		}
		ConstString Name() {
			return name;
		}
	};

	struct Atom : SExpr {
		Atom(ConstString s) 
			: data(s)
		{ }
		Atom() 
		{ }
		String Label() { return data; }
		SExpr* NewInstance() { return new Atom(data); }
		String data;
	};

	bool IsAtom(SExpr* x, ConstString s) {
		return x->Is<Atom>() && x->As<Atom>()->Label() == s;
	}

	struct SExprPrinter {
		int nindent;
		bool bindent;

		SExprPrinter(bool pretty = true) 
			: nindent(0), bindent(pretty) { }

		String operator()(SExpr* expr) {
			String r = "";
			if (expr->Is<Atom>()) {
				r += " ";
				r += expr->Label();
			}
			else {
				if (bindent) {
					r = "\n";
					for (int i=0; i < nindent; ++i)
						r += "  ";
				}

				if (expr->Is<PSExpr<int> >()) {
					r += expr->Label();
					r += "[";
					r += ToStr(expr->As<PSExpr<int> >()->val);
					r += "]";
				}
				else {
					r += "(";
					r += expr->Label();
					nindent++;
					for (int i=0; i < expr->GetNumChildren(); ++i) {
						r += operator()(expr->GetChild(i));
					}
					r += ")";
					nindent--;
				}
			}
			return r;
		} 
	};

	String PrettyPrint(SExpr* expr) {
		SExprPrinter p(true);
		return p(expr) + "\n";
	}

	String Print(SExpr* expr) {
		SExprPrinter p(false);
		return p(expr);
	}
}

#endif
